﻿using DotNetty.Transport.Channels;
using System;
using System.Text;
using System.Threading.Tasks;
using DotNetty.Buffers;
using DotNetty.Transport.Bootstrapping;
using YanShu.Utils;
using Newtonsoft.Json;
using System.Collections.Generic;
using YanShu.Constant;
using YanShu.Entity;

namespace YanShu.Handler
{
    public class ClientChannelHandler : SimpleChannelInboundHandler<ProxyMessage>
    {

        readonly private static ClientChannelHandler clientChannelHandler = new ClientChannelHandler();

        readonly private Bootstrap bootstrap;

        readonly private Bootstrap proxyBootstrap;

        public Client client;

        public static ClientChannelHandler Instance => clientChannelHandler;

        public ClientChannelHandler()
        {

        }
        public ClientChannelHandler(Bootstrap bootstrap, Bootstrap proxyBootstrap, Client client)
        {
            this.bootstrap = bootstrap;
            this.proxyBootstrap = proxyBootstrap;
            this.client = client;

        }


        protected override void ChannelRead0(IChannelHandlerContext ctx, ProxyMessage proxyMessage)
        {
            switch (proxyMessage.GetType())
            {
                case ProxyMessage.C_TYPE_AUTH:
                    HandleAuthMessage(ctx, proxyMessage);
                    break;
                case ProxyMessage.TYPE_CONNECT:
                    Task.Run(() => HandleConnectMessageAsync(ctx, proxyMessage));
                    break;
                case ProxyMessage.TYPE_DISCONNECT:
                    HandleDisconnectMessage(ctx, proxyMessage);
                    break;
                case ProxyMessage.P_TYPE_TRANSFER:
                    HandleTransferMessage(ctx, proxyMessage);
                    break;
                case ProxyMessage.TYPE_SIGNUP:
                    HandleSignUp(ctx, proxyMessage);
                    break;
                case ProxyMessage.C_TYPE_TOKEN_AUTH:
                    HandleTokenAuth(ctx, proxyMessage);
                    break;
                case ProxyMessage.TYPE_VERIFY_CODE:
                    HandleVerifyCode(ctx, proxyMessage);
                    break;
                case ProxyMessage.TYPE_RESET_PASSWORD:
                    HandleResetPassword(ctx, proxyMessage);
                    break;
                default:
                    break;
            }

        }

        private void HandleResetPassword(IChannelHandlerContext ctx, ProxyMessage proxyMessage)
        {
            String msg = Encoding.UTF8.GetString(proxyMessage.GetData());
            CommonDTO commonDTO = JsonConvert.DeserializeObject<CommonDTO>(msg);
            App.Current.Dispatcher.Invoke((Action)(() =>
            {
                if (commonDTO.GetCode().Equals(CommonDTO.SUCCESS))
                {
                    commonDTO.SetMsg("密码重置成功！");
                    Login.HideResetPwdGrid();
                }
                Login.HideGif();
                Login.ShowInfo(commonDTO.GetMsg());
            }));
        }

        private void HandleVerifyCode(IChannelHandlerContext ctx, ProxyMessage proxyMessage)
        {
            String result = Encoding.UTF8.GetString(proxyMessage.GetData());
            CommonDTO commonDTO = JsonConvert.DeserializeObject<CommonDTO>(result);
            if (commonDTO.GetCode().Equals(CommonDTO.SUCCESS))
            {
                App.ShowBalloonTip("提示","验证码已发送！");
            }
            else
            {
                App.Current.Dispatcher.Invoke((Action)(() =>
                {
                    Login.ShowLoading();
                    Login.HideGif();
                    Login.ShowInfo(commonDTO.GetMsg());
                    Login.ResetVerifyBtn(commonDTO.GetData().ToString());
                }));
            }
        }

        private void HandleTokenAuth(IChannelHandlerContext ctx, ProxyMessage proxyMessage)
        {
            String result = Encoding.UTF8.GetString(proxyMessage.GetData());
            CommonDTO commonDTO = JsonConvert.DeserializeObject<CommonDTO>(result);
            if (commonDTO.GetCode().Equals(CommonDTO.SUCCESS))
            {
                App.Current.Dispatcher.Invoke((Action)(() =>
                {
                    MainWindow.SwtichToIndex();
                    Index.Online();
                }));
            }
            else
            {
                App.Current.Dispatcher.Invoke((Action)(() =>
                {
                    MainWindow.SwtichToLogin();
                }));
            }
        }

        private void HandleSignUp(IChannelHandlerContext ctx, ProxyMessage proxyMessage)
        {
            String msg = Encoding.UTF8.GetString(proxyMessage.GetData());
            CommonDTO commonDTO = JsonConvert.DeserializeObject<CommonDTO>(msg);
            App.Current.Dispatcher.Invoke((Action)(() =>
            {
                if (commonDTO.GetCode().Equals(CommonDTO.SUCCESS))
                {
                    commonDTO.SetMsg("注册成功！");
                    Login.HideSignUpGrid();
                }
                Login.HideGif();
                Login.ShowInfo(commonDTO.GetMsg()) ;
            }));

        }

        // 登录授权响应
        private void HandleAuthMessage(IChannelHandlerContext ctx, ProxyMessage proxyMessage)
        {
            // IChannel channel = ctx.Channel;
            String data = Encoding.UTF8.GetString(proxyMessage.GetData());
            if (data.Equals("fail"))
            {
                App.Current.Dispatcher.Invoke((Action)(() =>
                {
                    Login.HideGif();
                    Login.ShowInfo("账户或密码不正确！");
                }));
                return;
            }
            //CommonEntity message = JsonConvert.DeserializeObject<CommonEntity>(data);
            CommonUtils.WriteConfig(data);
            //切换成主线程更换页面
            App.Current.Dispatcher.Invoke((Action)(() =>
            {
                MainWindow.SwtichToIndex();
            }));
        }


        /// <summary>
        /// 开启服务(报备代理信息。如需特定端口，便开启远程端口)
        /// </summary>
        public void OpenService(String ip, String port)
        {
            // 先填写本地配置信息。点击连接则发起远程开启用户服务端口
            IChannel channel = ClientChannelMannager.GetCmdChannel();
            if (channel.IsWritable)
            {
                String lanInfo = ip + ":" + port;
                //保存本地代理信息
                CommonEntity confEntity = CommonUtils.ReadAllConfig();
                confEntity.SetLanInfo(lanInfo);
                CommonUtils.WriteConfig(JsonConvert.SerializeObject(confEntity));
                //上报本地代理信息
                CommonEntity entity = new CommonEntity();
                String token = confEntity.GetToken();
                entity.SetToken(token);
                entity.SetLanInfo(lanInfo);

                String data = JsonConvert.SerializeObject(entity);

                ProxyMessage proxyMessage = new ProxyMessage();
                proxyMessage.SetType(ProxyMessage.C_TYPE_OPEN);
                proxyMessage.SetData(Encoding.UTF8.GetBytes(data));
                channel.WriteAndFlushAsync(proxyMessage);
            }
        }
        /// <summary>
        /// 登录
        /// </summary>
        public void LoginToServer(String account, String password)
        {
            IChannel channel = ClientChannelMannager.GetCmdChannel();
            ProxyMessage message = new ProxyMessage();
            message.SetType(ProxyMessage.C_TYPE_AUTH);
            CommonEntity entity = new CommonEntity();
            entity.SetAccount(account);
            entity.SetPassword(CommonUtils.CreateMD5Hash(password));
            String json = JsonConvert.SerializeObject(entity);
            message.SetData(Encoding.UTF8.GetBytes(json));
            channel.WriteAndFlushAsync(message);
        }
        /// <summary>
        /// 注册
        /// </summary>
        public void SignUpToServer(AccountDTO dto)
        {
            IChannel channel = ClientChannelMannager.GetCmdChannel();
            ProxyMessage message = new ProxyMessage();
            message.SetType(ProxyMessage.TYPE_SIGNUP);
            message.SetData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dto)));
            channel.WriteAndFlushAsync(message);
        }
        /// <summary>
        /// 重置密码
        /// </summary>
        public void ResetPassowrdToServer(AccountDTO dto)
        {
            IChannel channel = ClientChannelMannager.GetCmdChannel();
            ProxyMessage message = new ProxyMessage();
            message.SetType(ProxyMessage.TYPE_RESET_PASSWORD);
            message.SetData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dto)));
            channel.WriteAndFlushAsync(message);
        }

        /// <summary>
        /// 发送验证码
        /// </summary>
        public void VerifyCodeToServer(AccountDTO dto)
        {
            IChannel channel = ClientChannelMannager.GetCmdChannel();
            ProxyMessage message = new ProxyMessage();
            message.SetType(ProxyMessage.TYPE_VERIFY_CODE);
            message.SetData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dto)));
            channel.WriteAndFlushAsync(message);
        }

        /// <summary>
        /// Token授权
        /// </summary>
        public void TokenAuthToServer()
        {
            String token = CommonUtils.ReadConfig("token");
            if (null != token)
            {
                IChannel channel = ClientChannelMannager.GetCmdChannel();
                ProxyMessage proxyMessage = new ProxyMessage();
                proxyMessage.SetType(ProxyMessage.C_TYPE_TOKEN_AUTH);
                proxyMessage.SetData(Encoding.UTF8.GetBytes(token));
                channel.WriteAndFlushAsync(proxyMessage);
            }
            else
            {
                App.Current.Dispatcher.Invoke((Action)(() =>
                {
                    //授权失败则退出至登陆页面！
                    MainWindow.SwtichToLogin();
                }));
            }
        }

        // 处理数据传输
        private void HandleTransferMessage(IChannelHandlerContext ctx, ProxyMessage proxyMessage)
        {
            // 本地信道
            IChannel realServerChannel = ctx.Channel.GetAttribute(Constants.NEXT_CHANNEL).Get();
            if (realServerChannel != null)
            {
                IByteBuffer buf = ctx.Allocator.Buffer(proxyMessage.GetData().Length);
                buf.WriteBytes(proxyMessage.GetData());
                // 将远程代理请求数据转发到本地信道
                realServerChannel.WriteAndFlushAsync(buf);
            }
        }

        private void HandleDisconnectMessage(IChannelHandlerContext ctx, ProxyMessage proxyMessage)
        {
            // 移除本地信道
            IChannel realServerChannel = ctx.Channel.GetAttribute(Constants.NEXT_CHANNEL).Get();
            if (realServerChannel != null)
            {
                // 移除本地信道
                ctx.Channel.GetAttribute(Constants.NEXT_CHANNEL).Remove();
                // 保存远程信道
                ClientChannelMannager.ReturnProxyChanel(ctx.Channel);
                // 关闭本地信道
                realServerChannel.CloseAsync();
            }
        }

        private async Task HandleConnectMessageAsync(IChannelHandlerContext ctx, ProxyMessage proxyMessage)
        {
            // 远程信道
            IChannel cmdChannel = ctx.Channel;
            // 用户ID
            String userId = proxyMessage.GetUri();
            // 协议
            String requestDomain = Encoding.UTF8.GetString(proxyMessage.GetData());
            try
            {

                List<LanInfo> lanInfos = CommonUtils.ReadLanInfosConfig();

                foreach (LanInfo lanInfo in lanInfos)
                {   //符合当前用户访问的协议、域名
                    if (lanInfo.GetProxyStatus() == StatusConstants.PROXY_STATUS_OPEN && lanInfo.GetPublicDomain().Equals(requestDomain))
                    {
                        String ip = lanInfo.GetPrivateHost();
                        int port = int.Parse(lanInfo.GetPrivatePort());

                        // 连接本地服务器并获取本地信道
                        IChannel realServerChannel = await bootstrap.ConnectAsync(ip, port);

                        // 连接成功 
                        if (realServerChannel.IsWritable)
                        {


                            //获取远程信道
                            IChannel proxyChannel = await ClientChannelMannager.BorrowProxyChanelAsync(proxyBootstrap);


                            if (proxyChannel.IsWritable)
                            {
                                //将本地信道属性里添加到远程信道
                                realServerChannel.GetAttribute(Constants.NEXT_CHANNEL).Set(proxyChannel);
                                //将远程信道属性里添加本地信道
                                proxyChannel.GetAttribute(Constants.NEXT_CHANNEL).Set(realServerChannel);
                                String token = CommonUtils.ReadConfig("token");
                                //远程绑定
                                ProxyMessage message = new ProxyMessage();
                                message.SetType(ProxyMessage.TYPE_CONNECT);
                                message.SetUri(userId + "@" + token);
                                await proxyChannel.WriteAndFlushAsync(message);

                                realServerChannel.Configuration.SetOption(ChannelOption.AutoRead, true);
                                ClientChannelMannager.AddRealServerChannel(userId, realServerChannel);
                                ClientChannelMannager.SetRealServerChannelUserId(realServerChannel, userId);
                            }
                        }
                        else
                        {
                            // 本地连接失败，即断开连接 
                            ProxyMessage disconnectMessage = new ProxyMessage();
                            disconnectMessage.SetType(ProxyMessage.TYPE_DISCONNECT);
                            disconnectMessage.SetUri(userId);
                            _ = cmdChannel.WriteAndFlushAsync(disconnectMessage);
                        }

                    }
                }
            }
            catch (Exception e)
            {
                e.ToString();
                // 报错即断开连接
                ProxyMessage disconnectMessage = new ProxyMessage();
                disconnectMessage.SetType(ProxyMessage.TYPE_DISCONNECT);
                disconnectMessage.SetUri(userId);
                _ = cmdChannel.WriteAndFlushAsync(disconnectMessage);
            }
        }

        public override void ChannelWritabilityChanged(IChannelHandlerContext context)
        {

            IChannel realServerChannel = context.Channel.GetAttribute(Constants.NEXT_CHANNEL).Get();
            if (realServerChannel != null)
            {
                realServerChannel.Configuration.SetOption(ChannelOption.AutoRead, context.Channel.IsWritable);
            }

            base.ChannelWritabilityChanged(context);

        }


        public override void ChannelActive(IChannelHandlerContext context)
        {

        }

        public override void ChannelInactive(IChannelHandlerContext context)
        {
            try
            {
                // 控制连接
                if (ClientChannelMannager.GetCmdChannel() == context.Channel)
                {
                    ClientChannelMannager.SetCmdChannel(null);
                    App.Current.Dispatcher.Invoke((Action)(() =>
                    {
                        Index.Offline();
                    }));
                    client.ChannelInactive(context);
                    ClientChannelMannager.ClearRealServerChannels();
                }
                else
                {
                    // 数据传输连接
                    IChannel realServerChannel = context.Channel.GetAttribute(Constants.NEXT_CHANNEL).Get();
                    if (realServerChannel != null && realServerChannel.Active)
                    {
                        realServerChannel.CloseAsync();
                    }
                }

                ClientChannelMannager.RemoveProxyChanel(context.Channel);
                base.ChannelInactive(context);
            }
            catch (Exception e)
            {
                e.ToString();
            }
        }

        //心跳检测超时会触发该方法，暂不使用
        public override void UserEventTriggered(IChannelHandlerContext context, object evt)
        {
        }

        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            MainWindow.SetText("Client Exception: " + exception);
            context.CloseAsync();
        }


    }
}
